<!-- 表单 》 数字框 -->

<template>
  <el-form-item v-bind="formItem">
    <!-- 标签 -->
    <template v-if="hasLabelSlot" #label>
      <slot name="label" :label="formItem.label">{{ formItem.label }}</slot>
    </template>

    <div :class="className">
      <!-- 前缀 -->
      <span v-if="hasPrefix" :class="`${className}-prefix`">
        <slot name="prefix">{{ prefix }}</slot>
      </span>

      <el-input-number :key="updateTime" v-model="model" v-bind="numProps" @change="emitChange" @blur="toBlur" />

      <!-- 后缀 -->
      <span v-if="hasSuffix" :class="`${className}-suffix`">
        <slot name="suffix">{{ suffix }}</slot>
      </span>
    </div>
  </el-form-item>
</template>

<script>
// ============================== 导入依赖 ============================== //

import CORE from '@/CORE'
import mixForm from '../mixForm'
import FormInput from '../FormInput'

const name = 'FormNum'

// ============================== 导出组件 ============================== //

export default {
  /**
   * 名称定义 (实际使用名称)
   */
  name,

  /**
   * 混入列表 (配置选项合并)
   */
  mixins: [mixForm],

  /**
   * 属性注册 (抽取以便查阅)
   */
  props: CORE.WEB[name],

  /**
   * 计算属性 (慎用箭头函数)
   */
  computed: {
    ...FormInput.computed,

    /**
     * 小数位数
     */
    precision() {
      // dec1 到 dec10
      const array10 = Array(10).fill().map((_, i) => i + 1)
      for (const i of array10) {
        if (this[`dec${i}`]) return i
      }
      const { decNum } = this
      return decNum
    },

    /**
     * 数字框属性
     */
    numProps() {
      const { commonProps, className, min, max, step, onlyStep, noBtn, rightBtn, precision } = this
      return {
        ...commonProps,
        class: `${className}-num`,
        min,
        max,
        step,
        precision,
        stepStrictly: onlyStep,
        controls: !noBtn,
        controlsPosition: rightBtn ? 'right' : null,
      }
    },

    /**
     * 判断步长的倍数
     */
    multiple() {
      const { step } = this
      const str = `${step}`

      if (step && str.includes('.')) {
        const count = str.length - str.lastIndexOf('.')
        return 10 ** count
      }
      return 1
    },
  },

  /**
   * 监听属性 (深度 deep 首次 immediate)
   */
  watch: {
    /**
     * 监听最小值
     */
    async min() {
      await this.$nextTick()
      this.resetModel()
    },

    /**
     * 监听最大值
     */
    async max() {
      await this.$nextTick()
      this.resetModel()
    },
  },

  /**
   * 本地方法 (轻量重要在前)
   */
  methods: {
    /**
     * 是否不等 (可覆盖)
     */
    isDiff() {
      const { validNum, obj, prop, model, init, min, max, initMin, initMax, string } = this
      const value = obj[prop]

      // 判断是否有值，无值时是否有默认值或默认最小/大值
      if (value === 0 || value) return value !== (string ? `${model}` : model)
      if (validNum(init)) return true
      if (initMin && min > -Infinity) return true
      if (initMax && max < Infinity) return true
      if (model) return true
      return value !== model
    },

    /**
     * 设置内部绑定值 (可覆盖)
     */
    setModel() {
      const { validNum, obj, prop, init, EMPTY, min, max, initMin, initMax, step, onlyStep, multiple, string } = this
      const value = string ? Number(obj[prop] || '') : obj[prop]
      let model = EMPTY

      if (validNum(value)) {
        // 有效数字
        model = value
      } else if (CORE.isNumber(value)) {
        // 其他数字，是否纠正
        if (value < min) model = min
        else if (value > max) model = max
        else if (step && onlyStep) model = value - ((value * multiple) % (step * multiple)) / multiple
      } else {
        // 非数字，有无默认值
        if (validNum(init)) model = init
        else if (initMin && min > -Infinity) model = min
        else if (initMax && max < Infinity) model = max
      }

      this.model = model
      this.updateTime = Date.now()
    },

    /**
     * 重新设置内部绑定值
     */
    resetModel() {
      const { validNum, EMPTY, min, max, step, onlyStep, multiple } = this
      const value = this.model
      let model = EMPTY

      // 纠正数字
      if (!validNum(this.model)) {
        if (value < min) model = min
        else if (value > max) model = max
        else if (step && onlyStep) model = value - ((value * multiple) % (step * multiple)) / multiple
        this.model = model
        this.updateTime = Date.now()
      }

      // 触发更新
      this.emitChange()
    },

    /**
     * 获取最终回传值 (可覆盖)
     */
    getValue() {
      const { model, string } = this
      return string ? (CORE.isEmpty(model) ? '' : `${model}`) : model
    },

    /**
     * 是否物理相等 (如不等可直接用 value 赋值给 obj[bind] ) (可覆盖)
     */
    equals(value) {
      const { obj, prop, string } = this
      const bindValue = obj[prop]
      if (string) return +bindValue === (value ? +value : '')
      else return bindValue === value
    },

    /**
     * 是否为有效数字
     */
    validNum(value) {
      const { min, max, step, onlyStep, multiple } = this

      // 判断非数字
      if (!CORE.isNumber(value)) return false

      // 判断范围
      if (value < min || value > max) return false

      // 判断步长 (防止 1 % 0.1 不为 0 的精度问题，改为判断 10 % 1)
      if (step && onlyStep && (value * multiple) % (step * multiple)) return false

      return true
    },

    /**
     * 离开事件
     */
    toBlur() {
      this.$emit('blur', this.model)
    },
  },
}
</script>
